// 一、接口的基本使用
// interface Info{
//     name:string;
//     age:number;
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name}--${age}`
// }
// console.log(getInfo({name:"jack",age:22}));

// interface Info{
//     job:string;
//     name:string;
//     age:number;
// }
// const getInfo=({job,name,age}:Info)=>{
//     return `${job}--${name}--${age}`
// }
// console.log(getInfo({job:"间谍",name:"李佳升",age:19}));


// // 二、接口的可选属性
// interface Info{
//     name:string;
//     age?:number;
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name}--${age}`
// }
// // 如果age不设置成可选参数的话
// console.log(getInfo({name:"dell"}));  // typescript会提示报错
// // 如果age设置成可选参数的话
// console.log(getInfo({name:"dell"}));  // 不会报错 会输出  dell--undefined


// interface Info{
//     name?:string;
//     age:number;
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name}--${age}`
// }
// // 如果name不设置成可选参数的话
// console.log(getInfo({age:20}));  // typescript会提示报错
// // 如果name设置成可选参数的话
// console.log(getInfo({age:20}));  // 不会报错 会输出  undefined--20


// // 三、多余属性检查
// interface Info{
//     name:string;
//     age:number;
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name}--${age}`
// }
// // 当接口中定义的属性没有love时,多传递参数会报错提示love不存在Info类型中
// console.log(getInfo({name:"jack",age:20,love:"跳舞"}))

// interface Info{
//     name:string;
//     age:number;
//     love:"跳舞";
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name}--${age}`
// }
// // 当接口中定义的属性没有job时,多传递参数会报错提示job不存在Info类型中
// console.log(getInfo({name:"jack",age:20,love:"跳舞",job:"IT"}))


// 四、绕开多余属性检查
// (1)使用类型断言  关键字 as 后边跟上类型名Info
// interface Info{
//     name?:string;
//     age:number;
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name ? name+"" : ""}--${age}`
// }
// console.log(getInfo({name:"jack",age:20,love:"跳舞"} as Info));  // 输出jack--20

// interface Info{
//     name?:string;
//     age:number;
//     love:string;
// }
// const getInfo=({name,age,love}:Info)=>{
//     return `${name ? name+"" : ""}--${age}--${love}`
// }
// console.log(getInfo({name:"dell",age:22,love:"跳舞",job:"IT"} as Info));  // dell--22--跳舞



// (2)添加索引签名 [prop:string]:any
// interface Info{
//     name:string;
//     age:number;
//     [prop:string]:any
// }
// const getInfo=({name,age}:Info)=>{
//     return `${name}--${age}`
// }
// console.log(getInfo({name:"dell",age:20,love:"跳舞"})); // dell--20

// interface Info{
//     name:string;
//     age:number;
//     love:string;
//     [prop:string]:any;
// }
// const getInfo=({name,age,love}:Info)=>{
//     return `${name}--${age}--${love}`
// }
// console.log(getInfo({name:"dell",age:20,love:"跳舞",job:"IT"})); // dell--20--跳舞


// (3)利用类型兼容性
// interface Info{
//     name:string;
// }
// const getInfo=({name}:Info)=>{
//     return `${name}`
// }
// const text={name:"jack",age:20};
// console.log(getInfo(text));   // 输出jack

// 上面这种方法完美通过检查，我们将对象字面量赋给一个变量option，
// 然后getVegetables传入 option，这时没有报错。是因为直接将对象字面量传入函数，
// 和先赋给变量再将变量传入函数，这两种检查机制是不一样的，后者是因为类型兼容性。




// // 五、只读属性  设置只读属性后不能修改  会报错
// interface Info{
//     readonly 0:string;
//     1:number;
// }
// const info:Info={
//     0:"jack",
//     1:20,
// }
// console.log(info[0]="dell");  // 会报错  无法分配到0,因为它是只读属性
// console.log(info[1]=22);  // 不会报错  输出22



// // 六、函数类型
// interface Info{
//     (num1:number,num2:number):number;
// }
// const info:Info=(num1:number,num2:number)=>{
//     return num1+num2;
// }
// console.log(info(2,3));





// 接口的高阶用法


// // 七、索引类型
// interface Info{
//     [id:number]:string
// }
// const info1:Info={
//     // 0 和 1 是数字类型,所以不会报错
//     0:"jack",
//     1:"dell",
// }
// const info2:Info={
//     // a 和 b 不是数字类型,所以会报错
//     a:"jack",
//     b:"dell",
// }
// const info3:Info=["jack","dell"];  // 索引为数值类型，值为字符串类型。 不会报错



// 八、继承接口  使用extends继承接口的属性
// interface Info1{
//     name:string;
// }
// interface Info2 extends Info1{  // 继承了Info1的属性  现在有name和age两个属性
//     age:number;
// }
// interface Info3 extends Info1{  // 继承了Info1的属性  现在有name和love两个属性
//     love:string;
// }



// 九、混合类型接口
// interface Info {
//     (): void;
//     count: number; 
//   }
//   const getCounter = (): Info => {
//     const c = () => { 
//       c.count++;
//     };
//     c.count = 0;
//     return c;
//   };
//   const counter: Info = getCounter();
//   counter();
//   console.log(counter.count); // 输出 1
//   counter();
//   console.log(counter.count); // 输出 2




// 使用泛型拯救你的any



// // 十、简单使用
// const getArray = <T>(value:T,times:number=5): T[]=>{
//     return new Array(times).fill(value);
// }
// getArray<number[]>([1,2],3).forEach(item=>{
//     console.log(item);
//     console.log(item.length);
// })

// // 不指定类型根据传递的参数进行类型判断
// getArray([1,2],3).forEach(item=>{
//     console.log(item);
//     console.log(item.length);
// })


// 十一、泛型变量
// const getArray = <T, U>(param1: T, param2: U, times: number): [T, U][] => {
//     return new Array(times).fill([param1, param2]);
// };
// getArray(1, "a", 3).forEach(item => {
//     console.log(item[0].length); // 类型“number”上不存在属性“length”
//     console.log(item[1].toFixed(2)); // 属性“toFixed”在类型“string”上不存在
// });



// 十二、泛型函数类型
//(1)简单定义
// const getArray : <T> (arg:T,times : number) => T[] = (arg,times)=>{
//     return new Array(times).fill(arg);
// }
// console.log(getArray<string>("1",3))  // 输出数组  3个字符串3
// console.log(getArray(3,3))   // 输出数组 三个3


//(2)使用类型别名定义
// type GetArray = <T>(arg: T, times: number) => T[];
// const getArray: GetArray = <T>(arg: T, times: number): T[] => {
//   return new Array(times).fill(arg);
// };
// console.log(getArray([1,2,3],3))


//(3)使用接口的形式定义
// interface GetArray {
//     <T>(arg: T, times: number): T[];
// }
// const getArray: GetArray = <T>(arg: T, times: number): T[] => {
//     return new Array(times).fill(arg);
// };
// console.log(getArray(1,5))  //输出数组  5个1


//(4)将接口中泛型变量提升到接口最外层
// interface Info<T> {
//     (arg: T, times: number): T[],
//     tag: T
// }
// const getArray: Info<number | string> = (arg, times) => {
//     return new Array(times).fill(arg)
// }
// getArray.tag = 1;

// console.log(getArray(100, 3))  // 输出数组  3个100
// console.log(getArray("100", 3))  // 输出数组  3个字符串100



// 十三、泛型约束
// interface Info {
//     length: number;
// }
// const getLength = <T extends Info>(param: T): number => {
//     return param.length;
// };
// console.log(getLength("abc"));
// console.log(getLength([1, 2, 3]));
// console.log(getLength({ length: 3 }));
// // 以上三种都含有length属性,不会报错
// //   console.log(getLength(123));  // 不具有length属性   会报错




// 十四、在泛型约束中使用类型参数
// const getProps = (object, propName) => {
//     return object[propName];
// };
// const obj = { a: "aa", b: "bb" };
// console.log(getProps(obj, "c"));  // 不存在c属性  输出undefined



// const getProp = <T, K extends keyof T>(object: T, propName: K) => {
//     return object[propName];
// };
// const obj = { a: "aa", b: "bb" };
// console.log(getProp(obj, "a"));  // 输出 aa
// // console.log(getProp(obj, "c"));  // 会报错  输出undefined









